package com.ruihu.admin.handler;

import com.ruihu.admin.common.log.RequestLogObject;
import com.ruihu.admin.common.log.ResponseLogObject;
import com.ruihu.admin.constant.CommonConst;
import com.ruihu.admin.util.RequestTokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.MDC;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

@Slf4j
@Aspect
@Component
@Order(2)
public class MethodLogAspect extends AbstractLogAspect {

    @Pointcut("@annotation(com.ruihu.admin.common.anno.MethodLog)")
    public void methodLogPointCut() {
    }

    @Around("methodLogPointCut()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();

        String msg = "success";
        String traceId = MDC.get(CommonConst.Log.TRACE_ID);
        String eventName = this.getMethod(proceedingJoinPoint).getName();
        long userId = -1;
        if (RequestTokenUtil.getThreadLocalUser() != null)
            userId = RequestTokenUtil.getThreadLocalUser().getRequestUserId();
        List<Object> args = this.filterArgs(proceedingJoinPoint);

        RequestLogObject requestLogObject = new RequestLogObject()
                .setTraceId(traceId)
                .setEventName(eventName)
                .setUserId(userId)
                .setArgs(args);
        log.info("Request: {}", requestLogObject);

        Object result = null;
        try {
            result = proceedingJoinPoint.proceed();
        } catch (Exception ex) {
            msg = ex.getMessage();
            log.error(ex.getMessage(), ex);
            throw ex;
        } finally {
            long endTime = System.currentTimeMillis();

            ResponseLogObject responseLogObject = new ResponseLogObject()
                    .setTraceId(traceId)
                    .setEventName(eventName)
                    .setMsg(msg)
                    .setCostTime(endTime - startTime)
                    .setUserId(userId)
                    .setResult(result);
            String json = super.serializeResult(responseLogObject);
            log.info("Response: {}", json);
        }

        return result;
    }

    private List<Object> filterArgs(ProceedingJoinPoint joinPoint) {
        List<Object> argList = new ArrayList<>();
        if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
            for (Object arg : joinPoint.getArgs()) {
                if (Objects.nonNull(arg) && !super.isFilterObject(arg)) {
                    argList.add(arg);
                }
            }
        }

        return argList;
    }
}
